﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;

namespace RedRiver.Framework.XtraCommon.Models
{
    /// <summary>
    /// 枚举类型字典,如果存在 <see cref="DescriptionAttribute"/> 则用 Description 作为名称
    /// <remarks>由于枚举字段不能使用<see cref="DisplayNameAttribute"/>属性,只能使用Description属性</remarks>
    /// </summary>
    public class EnumDictionary : DictionaryBase, IDictionary
    {
        private static readonly Dictionary<Type, IDictionary> EnumDicts = new Dictionary<Type, IDictionary>();

        private readonly Type _enumType;
        private readonly bool _flagsAttr;

        /// <summary>
        /// Enum类型的基础类
        /// </summary>
        private readonly Type _underlyingType;

        /// <summary>
        /// 采用枚举元素的名称作为键值
        /// 否则采用枚举元素实例作为键值
        /// </summary>
        private bool _isEnumNameKey = true;

        private EnumDictionary(Type enumType)
        {
            if (!enumType.IsEnum)
            {
                throw new Exception("需要一个 Enum 类型");
            }
            _enumType = enumType;
            _underlyingType = Enum.GetUnderlyingType(_enumType);
            _flagsAttr = TypeDescriptor.GetAttributes(enumType)[typeof(FlagsAttribute)] != null;
            if (!EnumDicts.ContainsKey(enumType))
            {
                EnumDicts.Add(enumType, this);
            }
        }

        /// <summary>
        /// 构造函数,枚举类型 键/值对的集合
        /// 其中键值为 枚举字段名称
        /// </summary>
        /// <param name="enumInstance"></param>
        public EnumDictionary(Enum enumInstance)
            : this(enumInstance.GetType())
        {
            foreach (string enumKey in Enum.GetNames(_enumType))
            {
                FieldInfo fi = _enumType.GetField(enumKey);
                var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                InnerHashtable.Add(enumKey, (attributes.Length > 0) ? attributes[0].Description : enumKey);
            }
        }

        public bool IsEnumNameKey
        {
            get { return _isEnumNameKey; }
        }

        /// <summary>
        /// 获得默认的枚举字典内容
        /// </summary>
        /// <param name="enumInstance"></param>
        /// <returns></returns>
        public static object GetDictValue(Enum enumInstance)
        {
            Type enumType = enumInstance.GetType();
            if (EnumDicts.ContainsKey(enumType))
            {
                return EnumDicts[enumType][enumInstance];
            }
            return new EnumDictionary(enumInstance)[enumInstance];
        }

        /// <summary>
        /// 获得一个 枚举类型 键/值对的集合
        /// 其中键值为 枚举常量
        /// </summary>
        /// <param name="enumType"></param>
        /// <returns></returns>
        public static IDictionary GetEnumValuesDictionary(Type enumType)
        {
            var dict = new EnumDictionary(enumType);
            foreach (Enum enumValue in Enum.GetValues(enumType))
            {
                string enumName = enumValue.ToString();
                FieldInfo fi = enumType.GetField(enumName);
                var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                dict.InnerHashtable.Add(enumValue, (attributes.Length > 0) ? attributes[0].Description : enumName);
            }
            dict._isEnumNameKey = false;
            return dict;
        }

        /// <summary>
        /// 获得一个枚举基础类型的字典
        /// </summary>
        /// <param name="enumType"></param>
        /// <returns></returns>
        public static IDictionary GetUnderlyingTypeDictionary(Type enumType)
        {
            var dict = new EnumDictionary(enumType);
            foreach (Enum enumValue in Enum.GetValues(enumType))
            {
                string enumName = enumValue.ToString();
                FieldInfo fi = enumType.GetField(enumName);
                var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                dict.InnerHashtable.Add(dict.GetUnderlyingValue(enumValue),
                                        (attributes.Length > 0) ? attributes[0].Description : enumName);
            }
            dict._isEnumNameKey = false;
            return dict;
        }

        /// <summary>
        /// 转换Enum 实例到基础类型
        /// </summary>
        /// <param name="enumValue"></param>
        /// <returns></returns>
        private object GetUnderlyingValue(Enum enumValue)
        {
            if (_underlyingType == typeof(Int32))
            {
                return Convert.ToInt32(enumValue);
            }
            if (_underlyingType == typeof(SByte))
            {
                return Convert.ToSByte(enumValue);
            }
            if (_underlyingType == typeof(Byte))
            {
                return Convert.ToByte(enumValue);
            }
            if (_underlyingType == typeof(Int16))
            {
                return Convert.ToInt16(enumValue);
            }
            if (_underlyingType == typeof(Int64))
            {
                return Convert.ToInt64(enumValue);
            }
            if (_underlyingType == typeof(UInt16))
            {
                return Convert.ToUInt16(enumValue);
            }
            if (_underlyingType == typeof(UInt32))
            {
                return Convert.ToUInt32(enumValue);
            }
            if (_underlyingType == typeof(UInt64))
            {
                return Convert.ToUInt64(enumValue);
            }
            return Convert.ToInt32(enumValue);
        }

        #region IDictionary 成员

        public bool IsReadOnly
        {
            get { return true; }
        }

        public object this[object key]
        {
            get
            {
                if (InnerHashtable.Contains(key))
                {
                    return InnerHashtable[key];
                }
                //解析为枚举对象实例
                object enumKey = Enum.Parse(_enumType, key.ToString(), true);
                if (InnerHashtable.Contains(enumKey))
                {
                    return InnerHashtable[enumKey];
                }
                //解析为枚举对象基础类型
                object underlyingValue = GetUnderlyingValue((Enum)enumKey);
                if (InnerHashtable.ContainsKey(underlyingValue))
                {
                    return InnerHashtable[underlyingValue];
                }

                //枚举对象包含位域标志
                if (_flagsAttr)
                {
                    Array enumValues = Enum.GetValues(_enumType);
                    ulong keyValue = Convert.ToUInt64(enumKey);
                    var enumNames = new List<string>();
                    foreach (object enumValue in enumValues)
                    {
                        ulong enumItem = Convert.ToUInt64(enumValue);
                        if ((enumItem & keyValue) != 0)
                        {
                            if (IsEnumNameKey)
                            {
                                enumNames.Add(InnerHashtable[enumValue.ToString()].ToString());
                            }
                            else
                            {
                                enumNames.Add(InnerHashtable[enumValue].ToString());
                            }
                        }
                    }
                    //保存枚举位域组合名称
                    string enumCombinName = string.Join(",", enumNames.ToArray());
                    if (IsEnumNameKey)
                    {
                        InnerHashtable.Add(enumKey, enumCombinName);
                    }
                    else
                    {
                        InnerHashtable.Add(underlyingValue, enumCombinName);
                    }

                    return enumCombinName;
                }
                return enumKey;
            }
            set { }
        }

        public bool Contains(object key)
        {
            try
            {
                Enum.Parse(_enumType, key.ToString(), true);
                return true;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.ToString(), GetType().Name);
                return false;
            }
        }

        public ICollection Values
        {
            get { return InnerHashtable.Values; }
        }

        public ICollection Keys
        {
            get { return InnerHashtable.Keys; }
        }

        private string GetFlagsValue(Enum enumKey)
        {
            Array enumValues = Enum.GetValues(_enumType);
            ulong keyValue = Convert.ToUInt64(enumKey);
            var enumNames = new List<string>();
            foreach (object enumValue in enumValues)
            {
                ulong enumItem = Convert.ToUInt64(enumValue);
                if ((enumItem & keyValue) != 0)
                {
                    if (IsEnumNameKey)
                    {
                        enumNames.Add(InnerHashtable[enumValue.ToString()].ToString());
                    }
                    else
                    {
                        enumNames.Add(InnerHashtable[enumValue].ToString());
                    }
                }
            }
            //保存枚举位域组合名称
            string enumCombinName = string.Join(",", enumNames.ToArray());
            if (IsEnumNameKey)
            {
                InnerHashtable.Add(enumKey, enumCombinName);
            }
            else
            {
                InnerHashtable.Add(GetUnderlyingValue(enumKey), enumCombinName);
            }

            return enumCombinName;
        }

        #endregion
    }
}
